package interfaces.spawnMenu.costumize.weapons;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import interfaces.GUISource;
import interfaces.language.LanguageLabel;
import interfaces.menu.joinGame.gamesTable.GamesTable;
import interfaces.spawnMenu.costumize.CustomizeContainer;
import interfaces.superWidgets.InterfaceButton;
import interfaces.superWidgets.InterfaceLabel;
import interfaces.tables.ChoiceContainer;

import logic.common.player.Player;
import logic.common.team.Team;
import logic.ships.hunter.Hunter;
import logic.weapons.Weapon;
import logic.weapons.WeaponProperties;
import logic.weapons.WeaponType;
import logic.weapons.energyWeapons.EnergyWeapon;
import logic.weapons.projectileWeapons.ProjectileWeapon;
import logic.weapons.projectileWeapons.ProjectileProperties;
import logic.weapons.projectileWeapons.heatSeeker.HeatSeekerProperties;
import logic.weapons.projectileWeapons.heatSeeker.HeatSeekerWeapon;
import logic.weapons.projectileWeapons.missile.MissileWeapon;
import logic.weapons.projectileWeapons.missile.MissileProperties;
import main.InitGame;
import main.listeners.listenerClasses.LanguageListener;

import org.fenggui.decorator.background.PlainBackground;
import org.fenggui.event.ButtonPressedEvent;
import org.fenggui.event.IButtonPressedListener;
import org.fenggui.event.key.Key;
import org.fenggui.event.mouse.MouseButton;
import org.fenggui.event.mouse.MousePressedEvent;
import org.fenggui.event.mouse.MouseReleasedEvent;
import org.fenggui.util.Color;

import factories.WeaponFactory;
import fileHandling.language.Lang;
import fileHandling.language.LanguageLoader;
import fileHandling.language.interfaces.SpawnMenuText;
import fileHandling.language.properties.WeaponProps;

public class WeaponSelectContent extends CustomizeContainer<WeaponProperties> {
	
	private final int ENERGY = 0, HEATSEAKER = 1, MISSILE = 2;
	
	private ChoiceContainer<WeaponTableButton> energyWeaponChoice, heatseakerChoice, missileChoice;
	private int maxEnergyWeapons, maxHeatseakers, maxMissiles;
	private LanguageLabel noHunterLabel;
	private String selectedWeapon;
	private List<WeaponProperties> currentEnergyProps;
	private List<ProjectileProperties> currentHeatseakerProps, currentMissileProps;
	private WeaponInfoContent infoContent;
	private List<EnergyWeapon> selectedEnergyWeapons;
	private List<ProjectileWeapon> selectedHeatseakers, selectedMissiles;
	private Player player;
	
	private HashMap<ChoiceContainer<WeaponTableButton>, Integer> choiceBuyMap, choiceSellMap;
	private String buyText, sellText;
	
	public WeaponSelectContent(Team team, int width, int height, Player player) {
		super(team, width, height);
		this.player = player;
				
		maxValues = team.getMaxWeaponValues();
		
		selectedEnergyWeapons = new ArrayList<EnergyWeapon>();
		selectedHeatseakers = new ArrayList<ProjectileWeapon>();
		selectedMissiles = new ArrayList<ProjectileWeapon>();
		
		choiceBuyMap = new HashMap<ChoiceContainer<WeaponTableButton>, Integer>();
		choiceSellMap = new HashMap<ChoiceContainer<WeaponTableButton>, Integer>();
		
		InitGame.get().addLanguageListener(new LanguageListener() {
			@Override
			public void languageChanged() {
				changeLanguage();
			}
		});
		
		init();
		
		changeLanguage();
	}
	
	private void initNoHunterLabel() {
		noHunterLabel = new LanguageLabel(SpawnMenuText.Select_Hunter_first, GUISource.bigFont);
		noHunterLabel.setSize(height / 2, height / 10);
		noHunterLabel.setXY(height / 3, height / 2);
		addWidget(noHunterLabel);
	}
	
	private void init() {
		initNoHunterLabel();
		
		border = width / 30;
		
		Lang choice = SpawnMenuText.Available_Energy_Weapons;
		Lang select = SpawnMenuText.Selected_Energy_Weapons;
		int choiceWidth = width - border * 2;
		int choiceHeight = (height / 4) - border;
		int choiceX = border;
		int choiceY = height - border - choiceHeight;
		energyWeaponChoice = new ChoiceContainer<WeaponTableButton>(choiceWidth, choiceHeight, choiceX, choiceY, choice, select, 2);
		changeChoiceBGColor(energyWeaponChoice);
		
		choice = SpawnMenuText.Available_Heatseakers;
		select = SpawnMenuText.Selected_Heatseakers;
		choiceY -= choiceHeight + border;
		heatseakerChoice = new ChoiceContainer<WeaponTableButton>(choiceWidth, choiceHeight, choiceX, choiceY, choice, select, 2);
		changeChoiceBGColor(heatseakerChoice);
		
		choice = SpawnMenuText.Available_Missiles;
		select = SpawnMenuText.Selected_Missiles;
		choiceY -= choiceHeight + border;
		missileChoice = new ChoiceContainer<WeaponTableButton>(choiceWidth, choiceHeight, choiceX, choiceY, choice, select, 2);
		changeChoiceBGColor(missileChoice);
		
		infoX = border;
		infoY = border;
		infoWidth = width - infoX * 2;
		infoHeight = missileChoice.getY() - infoY - border;
		
		initWeapons(energyWeaponChoice, team.getEnergyWeaponNames(), ENERGY);
		initWeapons(heatseakerChoice, team.getHeatseakerNames(), HEATSEAKER);
		initWeapons(missileChoice, team.getMissileNames(), MISSILE);
		
		WeaponProperties props = selectableSettings.get(team.getEnergyWeaponNames().get(0));
		HashMap<Lang, String[]> keys = new HashMap<Lang, String[]>();
		keys.put(WeaponProps.Damage, new String[] { WeaponProps.Damage.toString() });
		keys.put(WeaponProps.ShootRate, new String[] { WeaponProps.ShootRate.toString() });
		keys.put(WeaponProps.WeaponFireSpeed, new String[] { WeaponProps.WeaponFireSpeed.toString() });
		
		List<Lang> order = new ArrayList<Lang>();
		order.add(WeaponProps.Damage);
		order.add(WeaponProps.ShootRate);
		order.add(WeaponProps.WeaponFireSpeed);
		
		infoContent = new WeaponInfoContent(infoWidth, infoHeight, infoX, infoY, props, maxValues, keys, order, player);
	}
	
	private void changeChoiceBGColor(ChoiceContainer<?> choiceContainer) {
		choiceContainer.getAppearance().add(new PlainBackground(GamesTable.GAMES_TABLE_BG_COLOR));
	}
	
	private void changeChoiceButtons(ChoiceContainer<WeaponTableButton> container) {
		container.changeButtonTexts(buyText, sellText);
	}
	
	private void initWeapons(final ChoiceContainer<WeaponTableButton> choiceContent, List<String> weaponNames, final int type) {
		for(final String weaponName : weaponNames) {
			IButtonPressedListener listener = new IButtonPressedListener() {
				@Override
				public void buttonPressed(ButtonPressedEvent e) {
					WeaponTableButton button = (WeaponTableButton)e.getTrigger();
					int weaponIndex = choiceContent.getButtonIndex(button);
					
					if(choiceContent.isSelectionButton(button)) 
						changeSelectedWeapon(weaponIndex, weaponName, type);
					else {
						selectedWeapon = weaponName;
						changeWeaponInfos();
					}
					
					if(choiceContent != energyWeaponChoice) energyWeaponChoice.removeCenterButtons();
					if(choiceContent != heatseakerChoice) heatseakerChoice.removeCenterButtons();
					if(choiceContent != missileChoice) missileChoice.removeCenterButtons();
				}
			};
			String wName = createWeaponProperties(weaponName).getWeaponName();
			WeaponTableButton weaponButton = new WeaponTableButton(wName, weaponName, listener, GUISource.bigFont);
			choiceContent.addChoiceButton(weaponButton);
		}
		
		choiceContent.addListenerToAddButton(getAddListener(choiceContent));
		choiceContent.addListenerToRemoveButton(getRemoveListener(choiceContent, type));
		
		int choiceButtonWidth = choiceContent.getWidth() / 7;
		choiceContent.getAddButton().setWidth(choiceButtonWidth);
		choiceContent.getRemoveButton().setWidth(choiceButtonWidth);
		int choiceButtonX = choiceContent.getWidth() / 2 - choiceButtonWidth / 2;
		choiceContent.getAddButton().setX(choiceButtonX);
		choiceContent.getRemoveButton().setX(choiceButtonX);
	}
	
	private IButtonPressedListener getAddListener(final ChoiceContainer<WeaponTableButton> choiceContent) {
		return new IButtonPressedListener() {
			@Override
			public void buttonPressed(ButtonPressedEvent e) {
				if(!choiceContent.addWasPossible()) return;
				WeaponTableButton selectedButton = choiceContent.getSelectedAddButton();
				String weaponName = selectedButton.getWeaponDirName();
				Weapon weapon = WeaponFactory.createHunterWeapon(team.getFraction(), weaponName, hunter);
				hunter.addWeapon(weapon, InitGame.get().isServer());
				int weaponCosts = weapon.getWeaponProperties().getCosts();
				assert(hunter.getPilot().getMoney() >= weaponCosts);
				hunter.getPilot().spendMoney(weaponCosts);
				if(weapon instanceof EnergyWeapon) {
					selectedEnergyWeapons.add((EnergyWeapon)weapon);
					int index = getNullIndex(currentEnergyProps);
					if(index == -1) return;
					currentEnergyProps.set(index, weapon.getWeaponProperties());
					changeSelectedWeapon(index, weaponName, ENERGY);
				} else {
					ProjectileWeapon projectileWeapon = (ProjectileWeapon)weapon;
					if(projectileWeapon instanceof HeatSeekerWeapon) {
						selectedHeatseakers.add(projectileWeapon);
						int index = getNullIndex(currentHeatseakerProps);
						if(index == -1) return;
						currentHeatseakerProps.set(index, (ProjectileProperties)weapon.getWeaponProperties());
						changeSelectedWeapon(index, weaponName, HEATSEAKER);
					} else if(projectileWeapon instanceof MissileWeapon) {
						selectedMissiles.add(projectileWeapon);
						int index = getNullIndex(currentMissileProps);
						if(index == -1) return;
						currentMissileProps.set(index, (ProjectileProperties)weapon.getWeaponProperties());
						changeSelectedWeapon(index, weaponName, MISSILE);
					}
				}
			}
		};
	}
	
	private IButtonPressedListener getRemoveListener(final ChoiceContainer<WeaponTableButton> choiceContent, final int type) {
		return new IButtonPressedListener() {
			@Override
			public void buttonPressed(ButtonPressedEvent e) {
				if(!choiceContent.removeWasPossible()) return;
				int buttonIndex = choiceContent.getLastRemovedIndex();
				switch(type) {
					case ENERGY :
						if(selectedEnergyWeapons.size() <= buttonIndex) return;
						removeWeapon(buttonIndex, selectedEnergyWeapons, currentEnergyProps);
						break;
					case HEATSEAKER :
						if(selectedHeatseakers.size() <= buttonIndex) return;
						removeWeapon(buttonIndex, selectedHeatseakers, currentHeatseakerProps);
						break;
					case MISSILE :
						if(selectedMissiles.size() <= buttonIndex) return;
						removeWeapon(buttonIndex, selectedMissiles, currentMissileProps);
						break;
				}
				removeWidget(infoContent);
				selectionLabel.setText("");
			}
		};
	}
	
	private void removeWeapon(int index, List<? extends Weapon> weapons, List<? extends WeaponProperties> props) {
		Weapon w = weapons.get(index);
		hunter.removeWeapon(w);
		hunter.getPilot().addMoney(w.getWeaponProperties().getCosts());
		weapons.remove(index);
		props.set(index, null);
	}
	
	private WeaponProperties getWeaponProperties(String weaponName) {
		String fraction = team.getFraction().toString().toLowerCase();
		String weaponPath = "data/fractions/" + fraction + "/hunterWeapons/" + weaponName + "/";
		return WeaponFactory.getWeaponProperties(weaponPath);
	}
	
	private WeaponProperties createWeaponProperties(String weaponName) {
		WeaponProperties props = getWeaponProperties(weaponName);
		if(props != null) selectableSettings.put(weaponName, props);
		return props;
	}
	
	@SuppressWarnings("unchecked")
	private void changeSelectedWeapon(int weaponIndex, String weaponName, int type) {
		@SuppressWarnings("rawtypes")
		List weaponInfos = null;
		ChoiceContainer<WeaponTableButton> choiceContent = null;
		switch(type) {
			case ENERGY : weaponInfos = currentEnergyProps;
						  choiceContent = energyWeaponChoice;
						  break;
			case HEATSEAKER : weaponInfos = currentHeatseakerProps;
							  choiceContent = heatseakerChoice;
							  break;
			case MISSILE : weaponInfos = currentMissileProps;
			  			   choiceContent = missileChoice;
			  			   break;
		}
		if(weaponInfos == null) return;
		if(weaponInfos.get(weaponIndex) == null) 
			weaponInfos.add(weaponIndex, getWeaponProperties(weaponName));
		
		if(!getContent().contains(infoContent)) addWidget(infoContent);
		WeaponProperties props = (WeaponProperties)weaponInfos.get(weaponIndex);
		infoContent.setProperties(props);
		infoContent.initUpgrades(true);
		
		selectionLabel.setText(selectionString + ": " + props.getWeaponName());
		
		changeAddRemoveButtons(choiceContent, -1, props.getCosts());
	}
	
	private void changeWeaponInfos() {
		if(!getContent().contains(infoContent)) addWidget(infoContent);
		WeaponProperties props = selectableSettings.get(selectedWeapon);
		infoContent.setProperties(props);
		infoContent.initUpgrades(false);
		selectionLabel.setText(selectionString + ": " + props.getWeaponName());
		
		ChoiceContainer<WeaponTableButton> choiceCont = energyWeaponChoice;
		if(props instanceof HeatSeekerProperties) choiceCont = heatseakerChoice;
		else if(props instanceof MissileProperties) choiceCont = missileChoice;
		changeAddRemoveButtons(choiceCont, props.getCosts(), -1);
	}
	
	private void changeAddRemoveButtons(ChoiceContainer<WeaponTableButton> content, int buyPrice, int sellPrice) {
		String addString = buyText + (buyPrice > 0 ? " (" + buyPrice + ")" : "");
		InterfaceButton addButton = content.getAddButton();
		addButton.setText(addString);
		
		boolean enoughMoney = hunter.getPilot().getMoney() >= buyPrice;
		Color color = (enoughMoney ? Color.BLACK : Color.RED);
		GUISource.setTextColor(addButton, color);
		addButton.setEnabled(enoughMoney);
		
		String removeString = sellText + (sellPrice > 0 ? " (" + sellPrice + ")" : "");
		content.getRemoveButton().setText(removeString);
		
		if(buyPrice > 0) choiceBuyMap.put(content, Integer.valueOf(buyPrice));
		if(sellPrice > 0) choiceSellMap.put(content, Integer.valueOf(sellPrice));
	}
	
	@Override
	public void changeHunter(Hunter hunter) {
		super.changeHunter(hunter);
		
		if(hunter == null) {
			initNoHunterLabel();
			return;
		}
		
		maxEnergyWeapons = hunter.getWeaponSlots(WeaponType.Energy).size();
		maxHeatseakers = hunter.getWeaponSlots(WeaponType.HeatSeeker).size();
		maxMissiles = hunter.getWeaponSlots(WeaponType.Missile).size();
		
		currentEnergyProps = new ArrayList<WeaponProperties>();
		initList(currentEnergyProps, maxEnergyWeapons);
		
		currentHeatseakerProps = new ArrayList<ProjectileProperties>();
		initList(currentHeatseakerProps, maxHeatseakers);
		
		currentMissileProps = new ArrayList<ProjectileProperties>();
		initList(currentMissileProps, maxMissiles);
		
		addWidget(energyWeaponChoice);
		addWidget(heatseakerChoice);
		addWidget(missileChoice);
		energyWeaponChoice.clearSelection();
		heatseakerChoice.clearSelection();
		missileChoice.clearSelection();
		
		energyWeaponChoice.setMaxSelect(maxEnergyWeapons);
		heatseakerChoice.setMaxSelect(maxHeatseakers);
		missileChoice.setMaxSelect(maxMissiles);
		
		selectionLabel = new InterfaceLabel(GUISource.bigFont);
		selectionLabel.setSize(infoContent.getWidth(), infoContent.getHeight() / 4);
		selectionLabel.setXY(infoContent.getX(), infoContent.getX() + infoContent.getHeight());
		addWidget(selectionLabel);
		selectionString = LanguageLoader.get(SpawnMenuText.Selected_Weapon);
		
		for(Weapon weapon : hunter.getWeapons(WeaponType.Energy)) {
			findButton(energyWeaponChoice.getChoiceButtons(), weapon.getName());
			performAdd(energyWeaponChoice);
		}
		
		for(Weapon weapon : hunter.getWeapons(WeaponType.HeatSeeker)) {
			findButton(heatseakerChoice.getChoiceButtons(), weapon.getName());
			performAdd(heatseakerChoice);
		}
		
		for(Weapon weapon : hunter.getWeapons(WeaponType.Missile)) {
			findButton(missileChoice.getChoiceButtons(), weapon.getName());
			performAdd(missileChoice);
		}
	}
	
	private void performAdd(ChoiceContainer<WeaponTableButton> cont) {
		Set<Key> set = new HashSet<Key>();
		cont.getAddListener().buttonPressed(new ButtonPressedEvent(cont.getAddButton(), set));
	}
	
	private void findButton(List<WeaponTableButton> buttons, String weaponName) {
		for(WeaponTableButton button : buttons) {
			if(button.getText().equals(weaponName)) {
				pushButton(button);
				break;
			}
		}
	}
	
	private void pushButton(InterfaceButton button) {
		int x = button.getX() + button.getWidth() / 2;
		int y = button.getY() + button.getHeight() / 2;
		Set<Key> set = new HashSet<Key>();
		MousePressedEvent press = new MousePressedEvent(button, x, y, MouseButton.LEFT, 1, set);
		button.mousePressed(press);
		MouseReleasedEvent release = new MouseReleasedEvent(button, x, y, MouseButton.LEFT, 1, set);
		button.mouseReleased(release);
	}
	
	@SuppressWarnings("rawtypes")
	private int getNullIndex(List list) {
		for(int i = 0; i < list.size(); i++) {
			if(list.get(i) == null) return i;
		}
		return -1;
	}
	
	@SuppressWarnings("unchecked")
	private void initList(@SuppressWarnings("rawtypes") List list, int max) {
		assert(list.isEmpty());
		for(int i = 0; i < max; i++) {
			list.add(null);
		}
	}
	
	@Override
	public void updateMoney(int newMoney) {
		infoContent.updateMoney(newMoney);
	}
	
	@Override
	public void restore() {
		
	}
	
	private void changeLanguage() {
		buyText = LanguageLoader.get(SpawnMenuText.Buy);
		sellText = LanguageLoader.get(SpawnMenuText.Sell);
		
		changeChoiceButtons(energyWeaponChoice);
		changeChoiceButtons(heatseakerChoice);
		changeChoiceButtons(missileChoice);
		
		for(ChoiceContainer<WeaponTableButton> content : choiceBuyMap.keySet()) {
			int buyPrice = choiceBuyMap.get(content).intValue();
			changeAddRemoveButtons(content, buyPrice, -1);
		}
		
		for(ChoiceContainer<WeaponTableButton> content : choiceSellMap.keySet()) {
			int sellPrice = choiceSellMap.get(content).intValue();
			changeAddRemoveButtons(content, -1, sellPrice);
		}
	}
	
	private void addWeaponButtons(ChoiceContainer<WeaponTableButton> choice, List<WeaponTableButton> buttons) {
		for(WeaponTableButton b : choice.getChoiceButtons()) {
			buttons.add(b);
		}
	}
	
	public List<WeaponTableButton> getAvailableWeaponButtons() {
		List<WeaponTableButton> buttons = new ArrayList<WeaponTableButton>();
		
		addWeaponButtons(energyWeaponChoice, buttons);
		addWeaponButtons(heatseakerChoice, buttons);
		addWeaponButtons(missileChoice, buttons);
		
		return buttons;
	}
}